package com.qiwenshare.stock.strategy;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiwenshare.stock.api.IStockDayInfoService;
import com.qiwenshare.stock.api.IStockWeekInfoService;
import com.qiwenshare.stock.domain.StockDayInfo;
import com.qiwenshare.stock.domain.StockInfo;
import com.qiwenshare.stock.domain.StockWeekInfo;
import com.qiwenshare.stock.service.StockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.List;

/**
 * @ClassName WeekBreakStrategy
 * @Description TODO
 * @Author peng.zhang
 * @Date 2023/6/13 17:08
 * @Version 1.0
 * 1. 如果在最近一次卖出信号发出后，MACD 线从上到下穿过 0 线，之后发出的买入信号将更为可信。
 * 当买入信号发出时，MACD 线未必在 0 线以下，但在最近一次价格下跌过程中，MACD 线应曾位于 0 线以下。
 * 2. 如果在最近一次买入信号发出后，MACD 线由下而上穿过 0 线，之后发出的卖出信号将更为可信。
 * 当卖出信号发出时，MACD 线未必在 0 线以上，但在最近一次价格上涨过程中，MACD 线应曾位于 0 线以上。
 * 3. 当股市处于牛市行情，尤其是在上涨的初始和主升段时，MACD 在发出买入信号前可能未曾位于 0 线以下，但由于市场行情十分强劲，投资者可以考虑进入市场。
 **/
@Slf4j
@Component
public class MacdStrategy {
    @Resource
    private StockService stockService;
    @Resource
    private IStockDayInfoService stockDayInfoService;

    /**
     * 买入点
     *
     * @param stockNum
     * @param date
     * @return
     */
    public double buyPrice(String stockNum, String date) {
        /**
         * 如果在最近一次卖出信号发出后，MACD 线从上到下穿过 0 线，之后发出的买入信号将更为可信。
         * 当买入信号发出时，MACD 线未必在 0 线以下，但在最近一次价格下跌过程中，MACD 线应曾位于 0 线以下。
         */
        try {
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.le("date", date);
            queryWrapper.eq("stock_num", stockNum);
            queryWrapper.orderByDesc("date");
            queryWrapper.last(" limit 2");
            List<StockDayInfo> threeList = stockDayInfoService.list(queryWrapper);
            if (threeList == null || threeList.size() < 2) {
                return -1;
            }
            StockDayInfo preDayInfo = threeList.get(1);
            StockDayInfo currentDayInfo = threeList.get(0);
            //金叉
            if (isDIFUpCrossDEA(preDayInfo, currentDayInfo)) {
                //拉升幅度
//                double difUpRadio = calculateUpRatio(preDayInfo.getDif(), currentDayInfo.getDif());
//                if ( difUpRadio> 0.2&&preDayInfo.getDif()<0) {
//                    log.info("pre diff={},dea={}", preDayInfo.getDif(), preDayInfo.getDea());
//                    log.info("cur diff={},dea={}", currentDayInfo.getDif(), currentDayInfo.getDea());
//                    log.info("difUpRadio ={}", difUpRadio);
//                    return currentDayInfo.getClose();
//                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 卖点
     *
     * @param stockNum
     * @param date
     * @return
     */
    public double sellPrice(String stockNum, String date) {
        //dif线从上往下穿过DEA
        try {
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.le("date", date);
            queryWrapper.eq("stock_num", stockNum);
            queryWrapper.orderByDesc("date");
            queryWrapper.last(" limit 2");
            List<StockDayInfo> threeList = stockDayInfoService.list(queryWrapper);
            if (threeList == null || threeList.size() < 2) {
                return -1;
            }
            StockDayInfo preDayInfo = threeList.get(1);
            StockDayInfo currentDayInfo = threeList.get(0);
            if (isDIFDownCrossDEA(preDayInfo, currentDayInfo)) {
                return currentDayInfo.getClose();
            }
        } catch (Exception e) {
        }
        return -1;
    }

    /**
     * macd死叉
     * 卖出信号
     *
     * @return
     */
    public static boolean isDIFDownCrossDEA(StockDayInfo preStockdayinfo, StockDayInfo currentStockdayinfo) {
        if (preStockdayinfo.getDif() > preStockdayinfo.getDea()
                && currentStockdayinfo.getDif() < currentStockdayinfo.getDea()) {
            return true;
        }
        return false;
    }

    /**
     * macd金叉
     * 买入信号
     *
     * @return
     */
    public static boolean isDIFUpCrossDEA(StockDayInfo preStockdayinfo, StockDayInfo currentStockdayinfo) {
        if (preStockdayinfo.getDif() < preStockdayinfo.getDea()
                && currentStockdayinfo.getDif() > currentStockdayinfo.getDea()) {
            return true;
        }
        return false;
    }

    /**
     * 计算拉升幅度
     *
     * @param start
     * @param end
     */
    public static double calculateUpRatio(double start, double end) {
        return Math.abs((end - start) / start);
    }

    public static void main(String[] args) {
        System.out.println(calculateUpRatio(-0.25, -0.21));
    }

    public void backTest(String start, String end) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DateUtil.parseDate(start));
        calendar.add(Calendar.DAY_OF_YEAR, 7);
        String nextWeekDay = DateUtil.format(calendar.getTime(), "YYYY-MM-dd");
        List<StockInfo> list = stockService.list();
        int successCount = 0;
        int failCount = 0;
        for (StockInfo s : list) {
            double buyPrice = buyPrice(s.getStockNum(), start);
            if (buyPrice > 0) {
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.le("date", nextWeekDay);
                queryWrapper.eq("stock_num", s.getStockNum());
                queryWrapper.orderByDesc("date");
                queryWrapper.last(" limit 1");
                StockDayInfo currentInfo = stockDayInfoService.getOne(queryWrapper);
                log.info("stockNum:{},stockName:{},buy price:{},current price:{},ratio:{}",
                        currentInfo.getStockNum(),
                        currentInfo.getStockName(),
                        buyPrice,
                        currentInfo.getClose(),
                        (currentInfo.getClose() - buyPrice) / buyPrice * 100
                );
                if (buyPrice < currentInfo.getClose()) {
                    successCount++;
                } else {
                    failCount++;
                }
            }
        }
        log.info("success count:{},fail count:{}", successCount, failCount);
    }

}
